package com.txw.javaCore.server.service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.json.JSON;
import com.google.gson.Gson;
import com.txw.javaCore.api.enums.Constant;
import com.txw.javaCore.api.response.StatusCode;
import com.txw.javaCore.model.entity.UserOrder;
import com.txw.javaCore.model.mapper.UserOrderMapper;
import com.txw.javaCore.model.request.OrderCancelRequest;
import org.joda.time.DateTime;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION: 商城平台用户订单实战Service层
 * @Author: 涂玄武
 * @DATE: 2021/1/29 16:51
 */
@Service
public class OrderService {

    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    private static final Snowflake SNOWFLAKE = new Snowflake(2,3);

    //死信队列超时时间：ms
    private static final Long TTL = 5000L;

    @Autowired
    private UserOrderMapper userOrderMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedissonClient redisson;

    @Autowired
    private Environment env;

    /**
     * 创建订单-RabbitMQ死信队列
     * @param order
     * @return
     * @throws Exception
     */
    public String addOrder(UserOrder order) throws Exception{
        String orderNo = Constant.OrderType.Item.getType() + SNOWFLAKE.nextIdStr();
        order.setOrderNo(orderNo);
        order.setCreateTime(DateTime.now().toDate());
        order.setPayStatus(Constant.OrderPayStatus.UnPay.getCode());
        int res = userOrderMapper.insertSelective(order);

        if (res > 0){
            rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
            rabbitTemplate.setExchange(env.getProperty("mq.order.basic.exchange.v2"));
            rabbitTemplate.setRoutingKey(env.getProperty("mq.order.basic.routing.key.v2"));

            String data = new Gson().toJson(order);
            rabbitTemplate.convertAndSend(data,message -> {
                MessageProperties mp = message.getMessageProperties();
                mp.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                mp.setExpiration(TTL.toString());

                log.info("--死信队列处理动态配置的任务 - 只针对延迟发送的逻辑-发出消息：msg={}",data);
                return message;
            });
        }
        return orderNo;
    }

    /**
     * 获取订单详情
     * @param orderNo
     * @return
     * @throws Exception
     */
    public UserOrder getOrderInfo(final String orderNo) throws Exception{
        return userOrderMapper.selectByOrderNo(orderNo);
    }

    /**
     * 创建订单-redisson的类延迟队列功能组件-下单时设定一个随机的失效时间(一般是某个业务时间范围内的，如 30-60分钟)
     * 用户订单超时未支付自动失效 （Redisson-RMapCache-元素淘汰机制）
     * @param order
     * @param ttl
     * @return
     * @throws Exception
     */
    public String addOrderV2(UserOrder order,final Long ttl) throws Exception{
        String orderNo=Constant.OrderType.Item.getType() + SNOWFLAKE.nextIdStr();
        order.setOrderNo(orderNo);
        order.setCreateTime(DateTime.now().toDate());
        order.setPayStatus(Constant.OrderPayStatus.UnPay.getCode());
        int res=userOrderMapper.insertSelective(order);
        if (res>0 && ttl>0){
            //TODO:MapCache - 元素的淘汰策略~ 带有元素淘汰（Eviction）机制的映射类允许针对一个映射中每个元素单独设定 有效时间 和 最长闲置时间
            //TODO:无形中提供了一种 “单独失效某个元素Value，而不是整个Key” 的机制:可以通过为某个元素指定存储时间从而决定该数据元素的存活时效，而
            //TODO:不存在所有数据元素共同的Key失效从而导致所有的数据元素都出现丢失的情况
            RMapCache<Integer, String> rMapCache = redisson.getMapCache(Constant.RedisMapCacheUserOrder);
            rMapCache.put(order.getId(),orderNo,ttl, TimeUnit.SECONDS);

            log.info("--订单信息进Redisson延迟队列，订单编号：{}，时间为ttl={} 秒",orderNo,ttl);
        }
        return orderNo;
    }

    /**
     *
     * @param request
     * @throws Exception
     */
    public void cancelOrder(OrderCancelRequest request) throws Exception{
        int res=userOrderMapper.updateCancelOrder(request.getOrderNo(),Constant.OrderPayStatus.UnPay.getCode(),Constant.OrderPayStatus.UserCancel.getCode());
        if (res>0){
            //TODO:用户去掉订单成功 - 方法1:基于rabbitmq取消死信队列中的订单信息（实现不了）

        }
    }

    /**
     * 取消订单：订单记录标记状态，同时移除延迟队列中的消息
     * @param request
     * @throws Exception
     */
    public void cancelOrderV2(OrderCancelRequest request) throws Exception{
        UserOrder entity = userOrderMapper.selectByOrderNo(request.getOrderNo());
        if (Objects.isNull(entity) || !Objects.equals(Constant.OrderPayStatus.UnPay.getCode(),entity.getPayStatus())){
           throw new RuntimeException(StatusCode.CancelOrderFail.getMsg());
        }

        int res = userOrderMapper.updateCancelOrder(request.getOrderNo(), Constant.OrderPayStatus.UnPay.getCode(), Constant.OrderPayStatus.UserCancel.getCode());
        if (res > 0){
            //TODO:用户去掉订单成功 - 方法2:基于Redisson的延迟队列移除消息
            RMapCache<Integer, String> rMapCache = redisson.getMapCache(Constant.RedisMapCacheUserOrder);
            rMapCache.remove(entity.getId());

            log.info("成功将当前用户订单信息从Redisson延迟队列移除: 订单编号={}",request.getOrderNo());
        }
    }
}
